/*
 Copyright (c) 2008 TPSi
 Alex Iskander
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 
 */

#ifndef BINDABLE_BINDABLE_Bindable_H_
#define BINDABLE_BINDABLE_Bindable_H_

#include <vector>
#include <set>
#include <string>
#include "DestructionListener.h"
#include "Mem.h"

namespace Bind
{
	
	class BindableObject;
	class Selector;
	
	/**
	 \class Bindable
	 \brief Represents a single element which can bind to some other element.
	 
	 The Bindable class includes basic functions for interaction of
	 properties, as well as some basic functionality for property management
	 through class BindableObject (which is a derivative of Bindable).
	 */
	class Bindable : public Mem
	{
	public:
		/**
		 \fn Bindable();
		 \brief Constructor.
		 */
		Bindable();
		
		/**
		 \fn set(Bindable &property, bool copy = true);
		 \brief Binds this property to another property.
		 
		 \note If copy is set, settings are propagated when this occurs. For
		 instance, the selector to use for auto-binding is copied from 
		 \c property to this Bindable. This allows you to use one
		 property to set the binding behavior of another. "copy" is set if using
		 the assignment operator. If using the set operator, copy defaults to false.
		 
		 \warning If you bind the property to another property that has a
		 bindTo selector, and copy is set to \e true, <em>the bindTo selector
		 for this property will be reset!</em>
		 */
		virtual void set(Bindable &property, bool copy = true);
		
		
		/**
		 \fn operator=(Bindable &property);
		 \brief Binds this property to another property.
		 */
		virtual Bindable &operator=(Bindable &property);
		
		
		/**
		 \fn operator=(Bindable *property);
		 \brief Binds this property to another property.
		 */
		virtual Bindable &operator=(Bindable *property);
		
		/**
		 \fn bake();
		 \brief Clears any bindings, but keeps the values.
		 
		 Only succeeds where bindings are direct; may not work for weakly-bound
		 items such as arrays. Behavior is undefined; currently, arrays will
		 cease to work, but in future, they may still be a weak link.
		 */
		virtual void bake();
		
		/**
		 \fn unset();
		 \brief Clears any bindings, but, if applicable, should not keep values.
		 
		 The property should be in as default of a state as it can be.
		 
		 Because Bindable has no value, this defaults to just calling bake.
		 */
		virtual void unset() { this->bake(); }
		
		/**
		 \fn setName(std::string name);
		 \brief Sets the name of this property.
		 
		 The name is meant to be a unique identifier for a property. In cases where
		 properties are managed (for binding entire objects), this name can be used to
		 reference the property. In other situations, the name can be used by responders
		 to identify the property (using the function getName).
		 
		 The default name is an empty string.
		 
		 \param name The name of the property.
		 */
		void setName(std::string name);
		
		/**
		 \fn getName();
		 \brief Returns the name of this property.
		 
		 \sa setName
		 */
		std::string getName();
		
		/**
		 \fn setParent(BindableObject *parent);
		 \brief Sets the parent of this property.
		 
		 The parent is the object to which the property belongs. The parent must be
		 a IBindableObject object; if the object to which the property belogns is not IBindable,
		 then the property's parent should be NULL.
		 */
		void setParent(BindableObject *parent);
		
		/**
		 \fn getParent() const;
		 \brief Gets the parent of this property.
		 
		 \sa setParent();
		 */
		BindableObject *getParent() const;
		
		/**
		 \fn setDepth(long depth);
		 \brief Sets the depth of this property.
		 
		 The depth is used to determine scope in a somewhat efficient way. If
		 the depth of an object being bound to is less than or equal to the
		 depth of the property, then binding may proceed.
		 */
		void setDepth(long depth);
		
		/**
		 \fn getDepth();
		 \brief Gets the depth of this property.
		 
		 \sa setDepth();
		 */
		long getDepth();
		
		/**
		 \fn bindTo(Selector *selector, bool autoDelete = true);
		 \brief Sets the selector to be used to auto-bind.
		 */
		Selector *bindTo(Selector *selector, bool autoDelete = true);
		
		/**
		 \fn bindTo();
		 \brief Returns the selector which is used to auto-bind this property.
		 */
		Selector *bindTo();
		
		/**
		 \fn isBindableObject();
		 \brief Returns if this is (or represents) a BindableObject object.
		 
		 Accompanies getBindable().
		 */
		virtual bool isBindableObject();
		
		/**
		 \fn getBindableObject();
		 \brief Gets the BindableObject object that this represents.
		 */
		virtual BindableObject *getBindableObject();
		
		/**
		 \fn addListener(Bindable *property);
		 \brief Adds a property which will \e listen to this property.
		 */
		virtual void addListener(Bindable *property);
		
		/**
		 \fn removeListener(Bindable *property, int max = 0);
		 \brief Removes a property that listens to this property.
		 
		 \param property The listener to remove.
		 \param max The maximum number of listeners to delete. 0 = no limit.
		 */
		virtual void removeListener(Bindable *property, int max = 0);
		
		/**
		 \fn addListeningTo(Bindable *property);
		 \brief Informs Bindable that it is listening to another property.
		 
		 Should not be called manually.
		 */
		void addListeningTo(Bindable *property);
		
		/**
		 \fn addListeningTo(Bindable *property);
		 \brief Informs Bindable that it is listening to another property.
		 
		 Should not be called manually.
		 */
		void removeListeningTo(Bindable *property);
		
		/**
		 \fn update();
		 \brief Called by sub-classes to indicate an update.
		 */
		virtual void update();
		
		/**
		 \fn updated(Bindable *property);
		 \brief Indicates that \c property has been updated.
		 */
		virtual void updated(Bindable *property);
		
		/**
		 \fn destroyed(Bindable *property);
		 \brief Indicates that \c property is being destroyed.
		 */
		virtual void destroyed(Bindable *property);
		
		/**
		 \fn onDestroy(DestructionListener *listener);
		 \brief Adds a listener which listens for the destruction of this
		 property.
		 */
		void onDestroy(DestructionListener *listener);
		
		/**
		 \fn cancelOnDestroy(DestructionListener *listener);
		 \brief Removes a listener which listens for the destruction of this
		 property.
		 */
		void cancelOnDestroy(DestructionListener *listener);
		
		/**
		 \fn inheritable(bool allow = true);
		 \brief Sets whether inheritance of this property should occur.
		 
		 In situations where this belongs to a BindableObject object, and that object
		 has children, this determines whether those children should "inherit"
		 this property (in the current implementation, this means able to find
		 it through recursive uptracing).
		 */
		virtual void inheritable(bool allow = true);
		
		/**
		 \fn getResponderCount();
		 \brief For testing for memory leaks.
		 */
		int getResponderCount();
		
		virtual ~Bindable();
		
		bool autoDeleteFormatter; //!< whether to automatically delete the 
		//!< formatter.
	protected:
		/**
		 \fn onBind(Bindable *property, bool copy);
		 \brief Called when binding occurs.
		 */
		virtual void onBind(Bindable *property, bool copy);
		
		
		/**
		 \fn onUnbind(Bindable *property);
		 \brief Called when unbinding occurs.
		 */
		virtual void onUnbind(Bindable *property);
		
		Bindable *boundTo; //!< The property (if any) we are bound to.
		
		std::vector<Bindable *> listeners;
		//!< Properties listening to us.
		
		std::vector<Bindable *> listeningTo;
		//!< Properties we are listening to.
		
		std::set<DestructionListener *> destructionListeners;
		//!< A set of functions waiting to hear of the property's destruction.
		
		BindableObject *parent;
		//!< The optional parent of this property.
		
		bool _inheritable;
		//!< Determines if this property should be inherited.
		
		long depth; //!< The level within the optional parent. For recordkeeping.
		
		std::string name; //!< The name of this property.
		
		Selector *bindFieldTo; //!< The name of the field to bind to.
		
		bool autoDeleteBindTo; //!< Whether to automatically delete the selector.
		
		bool isUpdating; //!< Protects against recursion.
		
		bool isSetting; //!< Protects against recursion.
	};
	
}


#endif

